home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / util / snopdos1 / part02 / snoopdos.c
C/C++ Source or Header  |  1990-10-14  |  32KB  |  1,147 lines

  1. /*
  2.  *        SNOOPDOS.C
  3.  *
  4.  *        (C) Copyright Eddy Carroll, May 1990. Freely distributable.
  5.  *
  6.  *        Snoopdos patches into dos.library and outputs a message to a
  7.  *        debugging window or file whenever a process calls certain DOS
  8.  *        functions.
  9.  *
  10.  *        Type snoopdos -h for a list of available options. See the
  11.  *        documentation for further details.
  12.  *
  13.  *        Compiles under Lattice C V5.04. I use flags: -cusq -j88i -ms -v
  14.  */
  15.  
  16. #ifndef LATTICE_50
  17. #include "system.h"
  18. #endif
  19.  
  20. /*
  21.  *        Assorted strings
  22.  */
  23. #define TITLE \
  24. "SnoopDos V1.0 (C) Copyright Eddy Carroll, Sept 1990. Freely distributable."
  25.  
  26. char *HEADER =
  27. "Process name          Func  Filename                                Mode Res."
  28. "\r\n"
  29. "------------          ----  --------                                ---- ----"
  30. "\r\n";
  31.  
  32. #define PORTNAME    "SnoopDos Port"
  33.  
  34. #define DEFWINDOW    "CON:0/0/640/120/"
  35.  
  36. /*
  37.  *        The following message array contains both colour and non-colour
  38.  *        versions of the various short message strings displayed.
  39.  */
  40. char *msgs[][2] = {
  41. /* Monochrome           Colour   */
  42. /* ----------           ------   */
  43.     "OLD ",                "OLD ",
  44.     "NEW ",                "\033[33mNEW\033[0m ",
  45.     "R/W ",                "\033[32mR/W\033[0m ",
  46.     "??? ",                "??? ",
  47.     "SHAR",                "SHAR",
  48.     "EXCL",                "\033[33mEXCL\033[0m",
  49.     "????",                "????",
  50.     "Okay\r\n",            "Okay\r\n",
  51.     "Fail\r\n",            "\033[33mFail\033[0m\r\n",
  52.     ">",                "\033[33m>\033[0m",
  53.     "> (Done)",            "> (Done)",
  54.     "Warning: Missed",    "\033[33mWarning:\033[0m Missed",
  55.     ">>>>\r\n",            ">>>>\r\n",
  56.     "Open",                "Open",
  57.     "Lock",                "\033[33mLock\033[0m",
  58.     "Load",                "\033[32mLoad\033[0m",
  59.     "Exec",                "\033[32mExec\033[0m",
  60.     "CD  ",                "CD  ",
  61.     "Del ",                "\033[33mDel\033[0m "
  62. };
  63.  
  64. #define TXT_OLD        msgs[ 0][colour]
  65. #define TXT_NEW        msgs[ 1][colour]
  66. #define TXT_R_W        msgs[ 2][colour]
  67. #define TXT_QM3        msgs[ 3][colour]
  68. #define TXT_SHAR    msgs[ 4][colour]
  69. #define TXT_EXCL    msgs[ 5][colour]
  70. #define TXT_QM4        msgs[ 6][colour]
  71. #define TXT_OKAY    msgs[ 7][colour]
  72. #define TXT_FAIL    msgs[ 8][colour]
  73. #define TXT_POINT    msgs[ 9][colour]
  74. #define TXT_DONE    msgs[10][colour]
  75. #define TXT_WARN    msgs[11][colour]
  76. #define TXT_NEST    msgs[12][colour]
  77. #define TXT_OPEN    msgs[13][colour]
  78. #define TXT_LOCK    msgs[14][colour]
  79. #define TXT_LOAD    msgs[15][colour]
  80. #define TXT_EXEC    msgs[16][colour]
  81. #define TXT_CURDIR    msgs[17][colour]
  82. #define TXT_DELETE    msgs[18][colour]
  83.  
  84. #define POINT(x)    ((x) ? TXT_POINT : " ")
  85.  
  86. /*
  87.  *        Now some standard system-type macros
  88.  */
  89. #define reg_d0    register __d0
  90. #define reg_d1    register __d1
  91. #define reg_d2    register __d2
  92. #define reg_d3    register __d3
  93. #define reg_a0    register __a0
  94.  
  95. #define BTOC(x)    (void *)(((ULONG)x) << 2)
  96.  
  97. #define D_S(name, type) char c_##name[sizeof(type)+3];\
  98.                         type *name = (type *)((long)(c_##name+3) & ~3)
  99.  
  100. extern __asm BPTR CallOpen(reg_d1 UBYTE *, reg_d2 int);
  101. extern __asm BPTR CallLock(reg_d1 UBYTE *, reg_d2 int);
  102. extern __asm BPTR CallLoadSeg(reg_d1 UBYTE *);
  103. extern __asm LONG CallExecute(reg_d1 UBYTE *, reg_d2 BPTR, reg_d3 BPTR);
  104. extern __asm BPTR CallCurrentDir(reg_d1 BPTR);
  105. extern __asm LONG CallDeleteFile(reg_d1 UBYTE *);
  106.  
  107. /*
  108.  *        Structure used to pass messages back and fro
  109.  */
  110. typedef struct {
  111.     struct Message msg;            /* Standard message header    */
  112.     struct Process *process;    /* Sending process id        */
  113.     int msgtype;                /* Message type, see below    */
  114.     int  data1;                    /* Data field 1                */ 
  115.     void *data2;                /* Data field 2                */
  116. } MYMSG;
  117.  
  118. /*
  119.  *        Now the various settings that can be set to affect the monitoring
  120.  */
  121. typedef struct {
  122.     int set_doopen;                /* If true, monitor Open()            */
  123.     int    set_dolock;                /* If true, monitor Lock()            */
  124.     int set_doloadseg;            /* If true, monitor LoadSeg()        */
  125.     int set_doexecute;            /* If true, monitor Execute()        */
  126.     int set_docurdir;            /* If true, monitor CurrentDir()    */
  127.     int set_dodelete;            /* If true, monitor DeleteFile()    */
  128.     int    set_showfullpath;        /* If true, display full paths        */
  129.     int set_sleepwait;            /* If true, sleep if necessary        */
  130.     int set_snoopactive;        /* If true, monitoring is active    */
  131.     int set_colour;                /* If true, use ANSI colour codes    */
  132. } SETTINGS;
  133.  
  134. /*
  135.  *        Default settings
  136.  */
  137. SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 };
  138.  
  139. /*
  140.  *        These defines allow the various settings to be accessed as
  141.  *        normal variables rather than structure members; this is purely
  142.  *        for convenience.
  143.  */
  144. #define doopen            settings.set_doopen
  145. #define dolock            settings.set_dolock
  146. #define doloadseg        settings.set_doloadseg
  147. #define doexecute        settings.set_doexecute
  148. #define docurdir        settings.set_docurdir
  149. #define dodelete        settings.set_dodelete
  150. #define showfullpath    settings.set_showfullpath
  151. #define sleepwait        settings.set_sleepwait
  152. #define snoopactive        settings.set_snoopactive
  153. #define colour            settings.set_colour
  154.  
  155. /*
  156.  *        Now the various message types that can be sent
  157.  */
  158. typedef enum {
  159.     MSG_QUIT,            /* Quit                     */
  160.     MSG_GETOPTIONS,        /* Read options                */
  161.     MSG_SETOPTIONS,        /* Update options            */
  162.     MSG_OPEN,            /* Open file                */
  163.     MSG_OPEN_DONE,        /* Open file completed        */
  164.     MSG_LOCK,            /* Lock file                */
  165.     MSG_LOCK_DONE,        /* Lock file completed        */
  166.     MSG_LOADSEG,        /* LoadSeg file                */
  167.     MSG_LOADSEG_DONE,    /* LoadSeg file completed    */
  168.     MSG_EXECUTE,        /* Execute command            */
  169.     MSG_CURDIR,            /* CurrentDir                */
  170.     MSG_DELETE,            /* DeleteFile                */
  171.     MSG_DELETE_DONE,    /* DeleteFile completed        */
  172. } MSGTYPES;
  173.  
  174. struct SignalSemaphore sem[1];
  175. struct MsgPort   *myport;        /* Pointer to background SnoopDos msg port    */
  176. struct MsgPort   *inport;        /* Pointer to our own message port            */
  177. struct Task      *snooptask;    /* Pointer to our own task                    */
  178. BPTR debugwin;                    /* Output file or window                    */
  179. BPTR stderr;                    /* Standard CLI console                        */
  180. int  extfile;                    /* True if output directed to external file    */
  181. char extfilename[100];            /* Name of window/file to open if specified    */
  182. char outbuf[800];                /* Output buffer used by myprintf() etc.    */
  183.  
  184. int missed_open_sig;            /* Signal to indicate Open() missed            */
  185. int missed_lock_sig;            /* Signal to indicate Lock() missed            */
  186. int missed_loadseg_sig;            /* Signal to indicate LoadSeg() missed        */
  187. int missed_execute_sig;            /* Signal to indicate Execute() missed        */
  188. int missed_curdir_sig;            /* Signal to indicate CurrentDir() missed    */
  189. int missed_delete_sig;            /* Signal to indicate DeleteFile() missed    */
  190. int portsig;                    /* Signal used by our public port            */
  191.  
  192. /**************************** Start of Functions ****************************/
  193.  
  194. /*
  195.  *        cleanup()
  196.  *        ---------
  197.  *        Cleans up all active resources and exits. If err is -1, then
  198.  *        returns instead of exiting.
  199.  */
  200. void cleanup(int err)
  201. {
  202.     static int called = 0;
  203.  
  204.     if (called++)        /* Make sure not called twice by accident */
  205.         return;
  206.  
  207.     if (inport)
  208.         DeletePort(inport);
  209.  
  210.     if (debugwin)
  211.         Close(debugwin);
  212.  
  213.     if (stderr)
  214.         Close(stderr);
  215.  
  216.     if (err != -1)
  217.         exit(err);
  218. }
  219.  
  220. /*
  221.  *        myprintf(), myfprintf()
  222.  *        -----------------------
  223.  *        Two low cost alternatives to printf that go directly to AmigaDOS
  224.  *        Note we deliberately use the pre-ANSI declaration, to avoid Lattice
  225.  *        kicking up a fuss about the wrong number of parameters.
  226.  */
  227. void myprintf(format, p1, p2, p3, p4, p5, p6)
  228. UBYTE *format;
  229. ULONG p1, p2, p3, p4, p5, p6;
  230. {
  231.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  232.     Write(Output(), outbuf, strlen(outbuf));
  233. }
  234.  
  235. void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
  236. BPTR file;
  237. char *format;
  238. ULONG p1, p2, p3, p4, p5, p6;
  239. {
  240.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  241.     Write(file, outbuf, strlen(outbuf));
  242. }
  243.  
  244. #define printf  myprintf
  245. #define fprintf myfprintf
  246.  
  247. /*
  248.  *        sendmsg()
  249.  *        ---------
  250.  *        Sends a message to myport, and waits for a reply to arrive.
  251.  *        A message type and some message data are all that need be provided
  252.  *        by the caller; the callee will provide the rest. Doesn't return
  253.  *        until a reply has been received.
  254.  */
  255. void sendmsg(int type, int data1, void *data2)
  256. {
  257.     MYMSG msg;
  258.     struct Process *me = (struct Process *)FindTask(0);
  259.     struct MsgPort *replyport = &me->pr_MsgPort;
  260.  
  261.     msg.msg.mn_Node.ln_Type = NT_MESSAGE;
  262.     msg.msg.mn_Length       = sizeof(MYMSG);
  263.     msg.msg.mn_ReplyPort    = replyport;
  264.     msg.process                = me;
  265.     msg.msgtype             = type;
  266.     msg.data1               = data1;
  267.     msg.data2               = data2;
  268.  
  269.     PutMsg(myport, &msg);
  270.     WaitPort(replyport);
  271.     GetMsg(replyport);
  272. }
  273.  
  274.  
  275. /*
  276.  *        getlockpath()
  277.  *        -------------
  278.  *        Returns a pointer to a string containing the full path for the
  279.  *        specified lock. You must copy the string before calling this
  280.  *        routine again.
  281.  */
  282. char *getlockpath(BPTR lock)
  283. {
  284.     struct Process *p = (struct Process *)FindTask(0L);
  285.     APTR oldwin = p->pr_WindowPtr;
  286.     static char path[300];
  287.     int pos = 299;
  288.     BPTR mylock;
  289.     char *name;
  290.     D_S(fib, struct FileInfoBlock);
  291.     int err = 0;
  292.  
  293.     p->pr_WindowPtr = (APTR)-1;    /* Disable error requesters */
  294.     mylock = DupLock(lock);
  295.  
  296.     path[pos] = '\0';
  297.  
  298.     do {
  299.         int len;
  300.         BPTR newlock;
  301.  
  302.         if (!Examine(mylock, fib))
  303.             err++;
  304.         name = fib->fib_FileName;
  305.         if (*name == '\0')
  306.             name = "RAM";        /* Workaround for old RAM: disk bug */
  307.         len = strlen(name);
  308.         pos = pos - len - 1;
  309.         newlock = ParentDir(mylock);
  310.         UnLock(mylock);
  311.         mylock = newlock;
  312.         strncpy(path + pos, name, len);
  313.         if (mylock)
  314.             path[pos + len] = '/';
  315.         else
  316.             path[pos + len] = ':';
  317.     } while (mylock);
  318.     p->pr_WindowPtr = oldwin;    /* Enable error requesters again */
  319.  
  320.     if (err) {
  321.         /*
  322.          *        Volume not present so have to be happy with just
  323.          *        returning the volume node instead.
  324.          */
  325.         struct FileLock *fl = BTOC(lock);
  326.         struct DeviceList *dl = BTOC(fl->fl_Volume);
  327.         UBYTE *name = BTOC(dl->dl_Name);
  328.  
  329.         strncpy(path, name + 1, *name);
  330.         path[*name] = '\0';
  331.         strcat(path, ":.../");
  332.         return (path);
  333.     } else
  334.         return (path + pos);
  335. }
  336.  
  337. /*
  338.  *        makepath()
  339.  *        ----------
  340.  *        Builds a full path string given a process ptr and a filename.
  341.  *        If the filename includes relative references (// or :)
  342.  *        these are handled also. The point of this is to resolve relative
  343.  *        directory references.
  344.  *
  345.  *        Returns TRUE if a new string was generated, FALSE if the existing
  346.  *        string didn't depend on the current directory (this doesn't affect
  347.  *        the output stored in buf though).
  348.  */
  349. int makepath(char *buf, BPTR curdir, char *filename)
  350. {
  351.     char *origfilename = filename;
  352.     int pos;
  353.     int doneroot = 0;
  354.  
  355.     /*
  356.      *        Special check for the 'current process console' file '*'
  357.      */
  358.     if (strcmp(filename, "*") == 0) {
  359.         strcpy(buf, filename);
  360.         return (FALSE);
  361.     }
  362.  
  363.     strcpy(buf, getlockpath(curdir));
  364.     pos = strlen(buf);
  365.  
  366.     for (;;) {
  367.         if (!doneroot && *filename == ':') {
  368.             /*
  369.              *        Path is relative to root
  370.              */
  371.             doneroot = 1;
  372.             for (pos = 0; buf[pos] && buf[pos] != ':'; pos++)
  373.                 ;
  374.             if (buf[pos] == ':')
  375.                 pos++;
  376.         } else if (*filename == '/') {
  377.             /*
  378.              *        Path is relative to parent directory; if none, then
  379.              *        remains the same.
  380.              */
  381.             int newpos = pos - 2;
  382.             while (newpos >= 0 && buf[newpos] != '/' && buf[newpos] != ':')
  383.                 newpos--;
  384.             if (newpos >= 0)
  385.                 pos = newpos + 1;
  386.         } else {
  387.             /*
  388.              *        No more special characters; just append what's left of
  389.              *        the filename.
  390.              */
  391.             if (!doneroot) {
  392.                 /*
  393.                  *        If the filename wasn't relative to the root
  394.                  *        directory, then make sure there are no device/volume
  395.                  *        references contained within it. We copy the original
  396.                  *        filename rather than the currently modified version
  397.                  *        since a volume name of /A: is legal and the / would
  398.                  *        be stripped off the modified name.
  399.                  */
  400.                 char *p;
  401.                 for (p = filename; *p; p++) {
  402.                     if (*p == ':') {
  403.                         strcpy(buf, origfilename);
  404.                         return (0);
  405.                     }
  406.                 }
  407.             }
  408.             strcpy(buf + pos, filename);
  409.             return (1);
  410.         }
  411.         filename++;
  412.     }
  413. }
  414.  
  415. /*
  416.  *        OutputLine()
  417.  *        ------------
  418.  *        Outputs a line of text in the main window, according to the
  419.  *        truncation mechanism used.
  420.  */
  421.  
  422. /*
  423.  *        mainloop()
  424.  *        ----------
  425.  *        This is the main event loop for SnoopDOS, where everything happens.
  426.  *        Control is passed here when SnoopDOS first starts up. When this
  427.  *        function returns, it terminates the background process.
  428.  */
  429. void mainloop(void)
  430. {
  431.     static char fullname[300];
  432.     int done   = 0;                    /* True if finished processing            */
  433.     int col0   = 1;                    /* True if cursor in column 0            */
  434.     int nested = 0;                    /* True if nested DOS calls                */
  435.  
  436. #define MISSED_NONE        0            /* Haven't missed anything    */
  437. #define MISSED_OPEN        (1 << 0)    /* Missed Open() call        */
  438. #define MISSED_LOCK        (1 << 1)    /* Missed Lock() call        */
  439. #define MISSED_LOADSEG    (1 << 2)    /* Missed LoadSeg() call    */
  440. #define MISSED_EXECUTE    (1 << 3)    /* Missed Execute() call    */
  441. #define MISSED_CURDIR    (1 << 4)    /* Missed CurrentDir() call    */
  442. #define MISSED_DELETE    (1 << 5)    /* Missed DeleteFile() call    */
  443.  
  444.     int missed = MISSED_NONE;        /* Was a DOS function missed? See above    */
  445.  
  446.     inport = CreatePort(PORTNAME, 0);
  447.     if (!inport) {
  448.         fprintf(stderr, "SnoopDos: Can't allocate message port.\n");
  449.         cleanup(-1);
  450.         return;
  451.     }
  452.  
  453.     myport  = inport;
  454.     portsig = 1 << myport->mp_SigBit;
  455.  
  456.     /*
  457.      *        Allocate signals
  458.      */
  459.     missed_open_sig       = 1 << AllocSignal(-1);
  460.     missed_lock_sig       = 1 << AllocSignal(-1);
  461.     missed_loadseg_sig = 1 << AllocSignal(-1);
  462.     missed_execute_sig = 1 << AllocSignal(-1);
  463.     missed_curdir_sig  = 1 << AllocSignal(-1);
  464.     missed_delete_sig  = 1 << AllocSignal(-1);
  465.  
  466.     if (    missed_open_sig    == -1  || missed_lock_sig    == -1  ||
  467.             missed_loadseg_sig == -1  || missed_execute_sig == -1  ||
  468.             missed_curdir_sig  == -1  || missed_delete_sig  == -1) {
  469.         fprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
  470.         cleanup(-1);
  471.         return;
  472.     }
  473.  
  474.     if (extfile) {
  475.         debugwin = Open(extfilename, MODE_NEWFILE);
  476.         if (!debugwin) {
  477.             fprintf(stderr, "SnoopDos: Can't open file %s for output.\n",
  478.                                 extfilename);
  479.             cleanup(-1);
  480.             return;
  481.         }
  482.         fprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
  483.     } else {
  484.         debugwin = Open(DEFWINDOW TITLE, MODE_NEWFILE);
  485.         if (!debugwin) {
  486.             fprintf(stderr, "SnoopDos: Can't open display window.\n");
  487.             cleanup(-1);
  488.             return;
  489.         }
  490.     }
  491.     Close(stderr); stderr = NULL;
  492.  
  493.     if (!extfile) {
  494.         fprintf(debugwin, "\n"
  495.     "Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit.\n\n");
  496.     };
  497.     fprintf(debugwin, HEADER);
  498.  
  499.     InitSemaphore(sem);
  500.     snooptask = FindTask(0L);
  501.     installdospatch();
  502.  
  503.     /*
  504.      *        Now just sit processing messages
  505.      */
  506.     while (!done) {
  507.         int mask;
  508.  
  509. #define SIGMASK    (portsig | missed_open_sig | missed_lock_sig | \
  510.                  missed_loadseg_sig | missed_execute_sig |     \
  511.                  missed_curdir_sig | missed_delete_sig |       \
  512.                  SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)
  513.  
  514.         mask = Wait(SIGMASK);
  515.  
  516.         if (mask & SIGBREAKF_CTRL_C)
  517.             done = 1;
  518.  
  519.         if (mask & SIGBREAKF_CTRL_D) {
  520.             if (snoopactive) {
  521.                 fprintf(debugwin,
  522. "\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
  523.                 snoopactive = 0;
  524.             }
  525.         }
  526.  
  527.         if (mask & SIGBREAKF_CTRL_E) {
  528.             if (!snoopactive) {
  529.                 fprintf(debugwin,
  530. "Snooping now enabled; type CTRL-D to disable it again.\r\n\r\n%s", HEADER);
  531.                 snoopactive = 1;
  532.             }
  533.         }
  534.  
  535.         if (mask & missed_open_sig)
  536.             missed |= MISSED_OPEN;
  537.  
  538.         if (mask & missed_lock_sig)
  539.             missed |= MISSED_LOCK;
  540.  
  541.         if (mask & missed_loadseg_sig)
  542.             missed |= MISSED_LOADSEG;
  543.  
  544.         if (mask & missed_execute_sig)
  545.             missed |= MISSED_EXECUTE;
  546.  
  547.         if (mask & missed_curdir_sig)
  548.             missed |= MISSED_CURDIR;
  549.  
  550.         if (mask & missed_delete_sig)
  551.             missed |= MISSED_DELETE;
  552.  
  553.         if (mask & portsig) {
  554.             MYMSG *msg;
  555.  
  556.             /*
  557.              *        Note in the following, there is some slightly tricky
  558.              *        stuff to handle the case where a DOS function calls
  559.              *        another DOS function internally. Under 1.3, this doesn't
  560.              *        happen, but it may under WB 2.0 and in addition, Jim
  561.              *        Goodnow's handy Rez utility patches LoadSeg() so that
  562.              *        it calls Open(). `nested' is true when a nested call
  563.              *        like this happens, and in this case, process names are
  564.              *        printed out prefixed by `> ' to indicate the nesting.
  565.              *
  566.              *        The nesting is detected because an incoming Open/Lock/
  567.              *        LoadSeg message will arrive when the cursor is positioned
  568.              *        to print a Fail/Okay result from a previous call (i.e.
  569.              *        col0 is false; the cursor is not in column 0). When a
  570.              *        result arrives in and the cursor IS in column 0, then
  571.              *        this must be the result from an earlier call so the
  572.              *        nesting is ended. Note that the semaphores used ensures
  573.              *        that this situation can only ever occur as a result of
  574.              *        nesting; other tasks will always wait until they are
  575.              *        properly in sync before sending messages to the main
  576.              *        Snoopdos process.
  577.              *
  578.              *        The more alert among you are probably saying something
  579.              *        like "Ah, but what if SnoopDos is configured to display
  580.              *        all DOS calls, even if it means forcing some tasks to
  581.              *        sleep until SnoopDos can process them? Then, when a
  582.              *        task calls LoadSeg() (say) and LoadSeg() calls Open(),
  583.              *        Open() will sleep forever waiting for the semaphore
  584.              *        Obtain()ed by LoadSeg() to become free again." Well,
  585.              *        in fact, it won't, since exec's ObtainSemaphore() call
  586.              *        will suceed even when the semaphore is in use, IF the
  587.              *        caller is the task that owns the semaphore -- and in such
  588.              *        a case, the caller will be the same task. (It took me a
  589.              *        while to figure out exactly what things were working --
  590.              *        it looked like I should get a serious lockup in certain
  591.              *        circumstances).
  592.              *        
  593.              */
  594.             while ((msg = (MYMSG *)GetMsg(myport)) != NULL) {
  595.                 /*
  596.                  *        Get the name of the process/command for
  597.                  *        printing out if necessary.
  598.                  */
  599.                 static char namebuf[256];
  600.                 UBYTE *procname = msg->process->pr_Task.tc_Node.ln_Name;
  601.                 struct CommandLineInterface *cli = BTOC(msg->process->pr_CLI);
  602.                 BPTR curdir = msg->process->pr_CurrentDir;
  603.                 UBYTE *s;
  604.                 UBYTE *filename;
  605.                 int newname;    /* If true, a new name was generated */
  606.  
  607.                 if (!col0 && (msg->msgtype == MSG_OPEN        ||
  608.                               msg->msgtype == MSG_LOCK        ||
  609.                               msg->msgtype == MSG_LOADSEG    ||
  610.                               msg->msgtype == MSG_CURDIR    ||
  611.                               msg->msgtype == MSG_DELETE)) {
  612.                     nested = 1;
  613.                     fprintf(debugwin, TXT_NEST);
  614.                 }
  615.                 if (cli && cli->cli_Module) {
  616.                     UBYTE *cliname = BTOC(cli->cli_CommandName);
  617.                     if (*cliname > 0) {
  618.                         /* Only use CLI name if it's not null */
  619.                         strncpy(namebuf+2, cliname + 1, *cliname);
  620.                         namebuf[*cliname+2] = '\0';
  621.                         procname = namebuf + 2;
  622.                         if (nested) {
  623.                             /*
  624.                              *        If we're not at column 0, then we're
  625.                              *        calling this DOS function from within
  626.                              *        another DOS function so indent display
  627.                              */
  628.                             procname = namebuf;
  629.                             procname[0] = '>';
  630.                             procname[1] = ' ';
  631.                         }
  632.                     }
  633.                 } else {
  634.                     if (nested) {
  635.                         sprintf(namebuf, "> %s", procname);
  636.                         procname = namebuf;
  637.                     }
  638.                 }
  639.  
  640.                 /*
  641.                  *        Now handle the message
  642.                  */
  643.                 filename = msg->data2;    /* Standard file name            */
  644.                 newname  = 0;            /* No problems expanding it        */
  645.  
  646.                 switch (msg->msgtype) {
  647.  
  648.                     case MSG_QUIT:
  649.                         done = 1;
  650.                         break;
  651.  
  652.                     case MSG_GETOPTIONS:
  653.                         memcpy(msg->data2, &settings, sizeof(SETTINGS));
  654.                         break;
  655.  
  656.                     case MSG_SETOPTIONS:
  657.                         memcpy(&settings, msg->data2, sizeof(SETTINGS));
  658.                         break;
  659.  
  660.                     case MSG_OPEN:
  661.                         if (msg->data1 == MODE_OLDFILE)
  662.                             s = TXT_OLD;
  663.                         else if (msg->data1 == MODE_NEWFILE)
  664.                             s = TXT_NEW;
  665.                         else if (msg->data1 == MODE_READWRITE)
  666.                             s = TXT_R_W;
  667.                         else
  668.                             s = TXT_QM3;
  669.  
  670.                         if (showfullpath) {
  671.                             newname = makepath(fullname, curdir, msg->data2);
  672.                             filename = fullname;
  673.                         }
  674.                         fprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  675.                                     TXT_OPEN, POINT(newname), filename, s);
  676.                         col0 = 0;
  677.                         break;  
  678.  
  679.                     case MSG_LOCK:
  680.                         if (msg->data1 == ACCESS_READ)
  681.                             s = TXT_SHAR;
  682.                         else if (msg->data1 == ACCESS_WRITE)
  683.                             s = TXT_EXCL;
  684.                         else
  685.                             s = TXT_QM4;
  686.  
  687.                         if (showfullpath) {
  688.                             newname = makepath(fullname, curdir, msg->data2);
  689.                             filename = fullname;
  690.                         }
  691.                         fprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  692.                                     TXT_LOCK, POINT(newname), filename, s);
  693.                         col0 = 0;
  694.                         break;
  695.  
  696.                     case MSG_LOADSEG:
  697.                         if (showfullpath) {
  698.                             newname = makepath(fullname, curdir, msg->data2);
  699.                             filename = fullname;
  700.                         }
  701.                         fprintf(debugwin, "%-21s %s %s%-44s ", procname,
  702.                                     TXT_LOAD, POINT(newname), filename);
  703.                         col0 = 0;
  704.                         break;
  705.  
  706.                     case MSG_EXECUTE:
  707.                         fprintf(debugwin, "%-21s %s  %s\r\n", procname,
  708.                                                     TXT_EXEC, filename);
  709.                         col0 = 1;
  710.                         break;
  711.  
  712.                     case MSG_CURDIR:
  713.                         {
  714.                             char *dirname = getlockpath(msg->data1);
  715.                             int i = strlen(dirname);
  716.  
  717.                             if (dirname[i-1] == '/')
  718.                                 dirname[i-1] = '\0';
  719.                             fprintf(debugwin, "%-21s %s  %s\r\n", procname,
  720.                                                     TXT_CURDIR, dirname);
  721.                             col0 = 1;
  722.                         }
  723.                         break;
  724.  
  725.                     case MSG_DELETE:
  726.                         if (showfullpath) {
  727.                             newname = makepath(fullname, curdir, msg->data2);
  728.                             filename = fullname;
  729.                         }
  730.                         fprintf(debugwin, "%-21s %s %s%-44s ", procname,
  731.                                     TXT_DELETE, POINT(newname), filename);
  732.                         col0 = 0;
  733.                         break;
  734.  
  735.                     case MSG_LOCK_DONE:
  736.                     case MSG_OPEN_DONE:
  737.                     case MSG_LOADSEG_DONE:
  738.                     case MSG_DELETE_DONE:
  739.                         if (col0 && nested) {
  740.                             fprintf(debugwin, "%-73s", TXT_DONE);
  741.                             nested = 0;
  742.                         }
  743.                         if (msg->data1)
  744.                             fprintf(debugwin, TXT_OKAY);
  745.                         else
  746.                             fprintf(debugwin, TXT_FAIL);
  747.                         col0 = 1;
  748.                         break;
  749.                 }
  750.                 ReplyMsg(msg);
  751.             }
  752.         }
  753.  
  754.         /*
  755.          *        Finally, check if we missed a DOS function. If we did,
  756.          *        AND we are in column 0, print out an appropriate message.
  757.          *        Otherwise, wait until next time. This stops the display
  758.          *        getting messed up when waiting to print a 'Fail' or 'Okay'.
  759.          */
  760.         if (missed) {
  761.             if (col0) {
  762.                 if (missed & MISSED_OPEN)
  763.                     fprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
  764.                 if (missed & MISSED_LOCK)
  765.                     fprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
  766.                 if (missed & MISSED_LOADSEG)
  767.                     fprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
  768.                 if (missed & MISSED_EXECUTE)
  769.                     fprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
  770.                 if (missed & MISSED_CURDIR)
  771.                     fprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
  772.                 if (missed & MISSED_DELETE)
  773.                     fprintf(debugwin, "%s a DeleteFile()\r\n", TXT_WARN);
  774.                 missed = MISSED_NONE;
  775.             }
  776.         }
  777.     }
  778.  
  779.     /*
  780.      *        Remove the port from the public ports list; this stops any
  781.      *        SnoopDos processes started up elsewhere from trying to
  782.      *        communicate with us (which may happen if this process can't
  783.      *        exit immediately).
  784.      */
  785.     RemPort(myport);
  786.     myport->mp_Node.ln_Name = NULL;
  787.  
  788.     /*
  789.      *        Now remove the dospatch, reply to all messages currently
  790.      *        pending and cleanup.
  791.      */
  792.     if (!uninstalldospatch()) {
  793.         /*
  794.          *        Someone else has patched DOSbase so print a message for
  795.          *        the user and keep trying every 5 seconds until we succeed.
  796.          */
  797.         int count = 0;
  798.  
  799.         fprintf(debugwin,
  800. "\r\n"
  801. "Someone else has patched dos.library so I'll have to wait until they quit."
  802. "\r\n");
  803.         if (!extfile) {
  804.             fprintf(debugwin, 
  805.   "This window will close shortly though to stop it from annoying you.\r\n");
  806.         }
  807.  
  808.         do {
  809.             Delay(250);    /* Wait 5 seconds */
  810.             if (debugwin) {
  811.                 count++;
  812.                 if (count > 2) {
  813.                     Close(debugwin);
  814.                     debugwin = NULL;
  815.                 }
  816.             }
  817.         } while (!uninstalldospatch());
  818.     }
  819.  
  820.     if (debugwin)
  821.         fprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
  822.  
  823.     /*
  824.      *        To make sure there is no code still executing in our task
  825.      *        before we exit, we allocate the semaphore used to ensure
  826.      *        exclusive access to the code, then wait a small time to allow
  827.      *        the other task a chance to execute the final instruction or two
  828.      *        left in our code after it releases the semaphore. This is not
  829.      *        foolproof :-( but it's better than nothing.
  830.      */
  831.     {
  832.         MYMSG *msg;
  833.         while (msg = (MYMSG *)GetMsg(myport))
  834.             ReplyMsg(msg);
  835.     }
  836.     ObtainSemaphore(sem);
  837.     Delay(10);        /* Wait 0.2 seconds */
  838.     cleanup(-1);
  839. }
  840.  
  841. /*
  842.  *        main()
  843.  *        ------
  844.  *        Mainline. Parses the command line and, if necessary, kicks off the
  845.  *        background task to do the real work.
  846.  */
  847. void main(int argc, char **argv)
  848. {
  849.     int error  = 0;            /* True if error detected in cmd line    */
  850.     int quit   = 0;            /* True if we want to quit                */
  851.     int colsel = 0;            /* True if colour option was specified    */
  852.  
  853.     /*
  854.      *        See if we are already active elsewhere; if yes, then read in
  855.      *        the default settings used by that process.
  856.      */
  857.  
  858.     myport = FindPort(PORTNAME);
  859.     if (myport)
  860.         sendmsg(MSG_GETOPTIONS, 0, &settings);
  861.  
  862.     while (argc > 1 && argv[1][0] == '-') {
  863.         int val;
  864.  
  865.         /* Make -X == -x0 and -x == -x1 */
  866.         if (argv[1][1] >= 'a' && argv[1][2] != '0')
  867.             val = 1;
  868.         else
  869.             val = 0;
  870.  
  871.         switch (tolower(argv[1][1])) {
  872.  
  873.             case 'a': colour         = val;
  874.                       colsel        = 1;   break;    /* Display ANSI colour    */
  875.             case 'c': docurdir        = val; break;    /* Monitor CurrentDir() */
  876.             case 'd': dodelete        = val; break;    /* Monitor DeleteFile()    */
  877.             case 'f': showfullpath    = val; break;    /* Show full filepath    */
  878.             case 'g': doloadseg        = val; break;    /* Monitor LoadSeg()    */
  879.             case 'h': error            = 1;   break;    /* Just print help msg    */
  880.             case 'l': dolock        = val; break;    /* Monitor Lock()        */
  881.             case 'm': snoopactive    = val; break;    /* Actively monitoring    */
  882.             case 'o': doopen        = val; break;    /* Monitor Open()        */
  883.             case 'q': quit            = 1;   break;    /* Ask SnoopDos to quit */
  884.             case 'r': error            = 2;   break;    /* Report settings        */
  885.             case 'w': sleepwait        = val; break;    /* Wait when necessary    */
  886.             case 'x': doexecute        = val; break;    /* Monitor Execute()    */
  887.             case 'z':
  888.                     if (argv[1][2]) {
  889.                         strcpy(extfilename, &argv[1][2]);
  890.                         extfile = 1;
  891.                     } else {
  892.                         argv++;
  893.                         argc--;
  894.                         if (argc > 1) {
  895.                             strcpy(extfile, argv[1]);
  896.                             extfile = 1;
  897.                         }
  898.                     }
  899.                     if (!extfile) {
  900.                         printf(
  901.         "-z must be followed by a filename. Type SnoopDos -h for help.\n");
  902.                         exit(5);
  903.                     }
  904.                     /*
  905.                      *        If outputting to a file, make colour off
  906.                      *        by default, rather than on.
  907.                      */
  908.                     if (colsel == 0)
  909.                         colour = 0;
  910.                     break;
  911.  
  912.             default:
  913.                 printf("Unrecognised option %s. Type Snoopdos -h for help.\n",
  914.                                                                     argv[1]);
  915.                 exit(5);
  916.         }
  917.         argv++; argc--;
  918.     }
  919.  
  920.     if (argc > 1)
  921.         error = 1;
  922.  
  923.     if (error) {
  924.         if (error == 1) {
  925.             printf(TITLE "\n\n"
  926. "SnoopDos monitors calls made to various AmigaDOS functions by all processes\n"
  927. "on the system. The following options are available. Use -x1 or just -x to\n"
  928. "enable an option, -X or -x0 to disable that option. Current settings in (-)."
  929. "\n\n");
  930.         } else
  931.             printf("Current SnoopDos settings:\n\n");
  932.  
  933. #define O(x,y) printf(x, y ? "(on)" : "(off)")
  934. O("    -a  Use ANSI colour sequences                     %s\n", colour);
  935. O("    -c  Monitor CurrentDir() calls                    %s\n", docurdir);
  936. O("    -d  Monitor DeleteFile() calls                    %s\n", dodelete);
  937. O("    -f  Display full filename paths                   %s\n", showfullpath);
  938. O("    -g  Monitor LoadSeg() calls                       %s\n", doloadseg);
  939. O("    -l  Monitor Lock() calls                          %s\n", dolock);
  940. O("    -m  Globally enable/disable monitoring            %s\n", snoopactive);
  941. O("    -o  Monitor Open() calls                          %s\n", doopen);
  942. O("    -w  Display all activity, sleeping if necessary   %s\n", sleepwait);
  943. O("    -x  Monitor Execute() calls                       %s\n", doexecute);
  944.  
  945.         if (error == 1) {
  946.             printf("\n"
  947. "    -h  Print out this help message\n"
  948. "    -q  Tell SnoopDos to quit\n"
  949. "    -r  Report current SnoopDos settings\n" 
  950. "    -z% Output to file % (e.g. -zCON:0/0/640/100/SnoopDos or -zSER:)\n"
  951. "\n");
  952.         }
  953.         cleanup(5);
  954.     }
  955.  
  956.     /*
  957.      *        First see are we already installed. If so, send a copy of the
  958.      *        updated settings to the background process, or send a QUIT
  959.      *        message if the user wanted to quit.
  960.      */
  961.     if (myport) {
  962.         if (quit)
  963.             sendmsg(MSG_QUIT, 0, 0);
  964.         else
  965.             sendmsg(MSG_SETOPTIONS, 0, &settings);
  966.         cleanup(0);
  967.     }
  968.  
  969.     /*
  970.      *        If user wanted to quit and we weren't already running, just
  971.      *        quit without doing anything.
  972.      */
  973.     if (quit) {
  974.         printf("There is no background SnoopDos process to tell to quit!\n");
  975.         cleanup(0);
  976.     }
  977.  
  978.     /*
  979.      *        Not installed, so install ourselves. First of all though, we
  980.      *        kick ourselves into the background and return control to the
  981.      *        calling CLI. We need to do this before we start creating ports
  982.      *        and opening files, to prevent Exec and AmigaDOS getting
  983.      *        confused about which task we are.
  984.      *
  985.      *        Also open stderr channel for the new task to output fatal
  986.      *        error messages to; the new task takes responsibility for closing
  987.      *        this channel.
  988.      */
  989.     stderr = Open("*", MODE_NEWFILE);
  990.     if (!res("SnoopDos", 5, mainloop, 4000)) {
  991.         printf("Couldn't spawn background SnoopDos task.\n");
  992.         cleanup(10);
  993.     }
  994. }
  995.  
  996. /*
  997.  *        NewOpen()
  998.  *        ---------
  999.  *        This is the new Open() code. It checks to see if the calling task
  1000.  *        is actually the SnoopDos task; if it is, then it essentially does
  1001.  *        nothing (this stops any possible deadlock occurring). Otherwise,
  1002.  *        it sends a message to the task with the pertinent info.
  1003.  *
  1004.  *        If sleeping is disabled then if the semaphore isn't immediately
  1005.  *        available, a signal is sent to the Snoopdos task telling it that
  1006.  *        it's missed trapping a function.
  1007.  */
  1008. __asm BPTR NewOpen(reg_d1 char *filename, reg_d2 int mode)
  1009. {
  1010.     BPTR filehandle;
  1011.     geta4();
  1012.     if (snoopactive && doopen && FindTask(0) != snooptask) {
  1013.         if (sleepwait)
  1014.             ObtainSemaphore(sem);
  1015.         else if (!AttemptSemaphore(sem)) {
  1016.             Signal(snooptask, missed_open_sig);
  1017.             return (CallOpen(filename, mode));
  1018.         }
  1019.         sendmsg(MSG_OPEN, mode, filename);
  1020.         filehandle = CallOpen(filename, mode);
  1021.         sendmsg(MSG_OPEN_DONE, filehandle, 0);
  1022.         ReleaseSemaphore(sem);
  1023.         return (filehandle);
  1024.     } else {
  1025.         return (CallOpen(filename, mode));
  1026.     }
  1027. }
  1028. /*
  1029.  *        NewLock()
  1030.  *        ---------
  1031.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1032.  *        equally here.
  1033.  */
  1034. __asm BPTR NewLock(reg_d1 char *filename, reg_d2 int mode)
  1035. {
  1036.     BPTR lock;
  1037.     geta4();
  1038.     if (snoopactive && dolock && FindTask(0) != snooptask) {
  1039.         if (sleepwait)
  1040.             ObtainSemaphore(sem);
  1041.         else if (!AttemptSemaphore(sem)) {
  1042.             Signal(snooptask, missed_lock_sig);
  1043.             return (CallLock(filename, mode));
  1044.         }
  1045.         sendmsg(MSG_LOCK, mode, filename);
  1046.         lock = CallLock(filename, mode);
  1047.         sendmsg(MSG_LOCK_DONE, lock, 0);
  1048.         ReleaseSemaphore(sem);
  1049.         return (lock);
  1050.     } else {
  1051.         return (CallLock(filename, mode));
  1052.     }
  1053. }
  1054.  
  1055. /*
  1056.  *        NewLoadSeg()
  1057.  *        ------------
  1058.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1059.  *        equally here.
  1060.  */
  1061. __asm BPTR NewLoadSeg(reg_d1 char *filename)
  1062. {
  1063.     BPTR seglist;
  1064.     geta4();
  1065.     if (snoopactive && doloadseg && FindTask(0) != snooptask) {
  1066.         if (sleepwait)
  1067.             ObtainSemaphore(sem);
  1068.         else if (!AttemptSemaphore(sem)) {
  1069.             Signal(snooptask, missed_loadseg_sig);
  1070.             return (CallLoadSeg(filename));
  1071.         }
  1072.         sendmsg(MSG_LOADSEG, 0, filename);
  1073.         seglist = CallLoadSeg(filename);
  1074.         sendmsg(MSG_LOADSEG_DONE, seglist, 0);
  1075.         ReleaseSemaphore(sem);
  1076.         return (seglist);
  1077.     } else {
  1078.         return (CallLoadSeg(filename));
  1079.     }
  1080. }
  1081.  
  1082. /*
  1083.  *        NewExecute()
  1084.  *        ------------
  1085.  *        Replacement for Execute()
  1086.  */
  1087. __asm LONG NewExecute(reg_d1 char *command, reg_d2 BPTR in, reg_d3 BPTR out)
  1088. {
  1089.     geta4();
  1090.     if (snoopactive && doexecute && FindTask(0) != snooptask) {
  1091.         if (sleepwait)
  1092.             ObtainSemaphore(sem);
  1093.         else if (!AttemptSemaphore(sem)) {
  1094.             Signal(snooptask, missed_execute_sig);
  1095.             return(CallExecute(command, in, out));
  1096.         }
  1097.         sendmsg(MSG_EXECUTE, 0, command);
  1098.         ReleaseSemaphore(sem);
  1099.     }
  1100.     return(CallExecute(command, in, out));
  1101. }
  1102.  
  1103. /*
  1104.  *        NewCurrentDir()
  1105.  *        ---------------
  1106.  *        Replacement for CurrentDir()
  1107.  */
  1108. __asm BPTR NewCurrentDir(reg_d1 BPTR newlock)
  1109. {
  1110.     geta4();
  1111.     if (snoopactive && docurdir && FindTask(0) != snooptask) {
  1112.         if (sleepwait)
  1113.             ObtainSemaphore(sem);
  1114.         else if (!AttemptSemaphore(sem)) {
  1115.             Signal(snooptask, missed_curdir_sig);
  1116.             return(CallCurrentDir(newlock));
  1117.         }
  1118.         sendmsg(MSG_CURDIR, newlock, 0);
  1119.         ReleaseSemaphore(sem);
  1120.     }
  1121.     return(CallCurrentDir(newlock));
  1122. }
  1123.  
  1124. /*
  1125.  *        NewDeleteFile()
  1126.  *        ---------------
  1127.  *        Replacement for DeleteFile()
  1128.  */
  1129. __asm LONG NewDeleteFile(reg_d1 char *filename)
  1130. {
  1131.     LONG success;
  1132.     geta4();
  1133.     if (snoopactive && dodelete && FindTask(0) != snooptask) {
  1134.         if (sleepwait)
  1135.             ObtainSemaphore(sem);
  1136.         else if (!AttemptSemaphore(sem)) {
  1137.             Signal(snooptask, missed_delete_sig);
  1138.             return(CallDeleteFile(filename));
  1139.         }
  1140.         sendmsg(MSG_DELETE, 0, filename);
  1141.         success = CallDeleteFile(filename);
  1142.         sendmsg(MSG_DELETE_DONE, success, 0);
  1143.         ReleaseSemaphore(sem);
  1144.     } else
  1145.         return(CallDeleteFile(filename));
  1146. }
  1147.